home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Games of Daze
/
Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso
/
x2ftp
/
msdos
/
mxcode
/
muspl150
/
adlib.c
next >
Wrap
C/C++ Source or Header
|
1994-10-30
|
10KB
|
401 lines
/*
* Name: Adlib Interface Module
* Version: 1.40
* Author: Vladimir Arnost (QA-Software)
* Last revision: Oct-30-1994
* Compiler: Borland C++ 3.1
*
*/
/*
* Revision History:
*
* Aug-8-1994 V1.00 V.Arnost
* Written from scratch
* Aug-9-1994 V1.10 V.Arnost
* Added stereo capabilities
* Aug-13-1994 V1.20 V.Arnost
* Stereo capabilities made functional
* Aug-24-1994 V1.30 V.Arnost
* Added Adlib and SB Pro II detection
* Oct-30-1994 V1.40 V.Arnost
* Added BLASTER variable parsing
*/
#include "adlib.h"
#include <dos.h>
#include <stdlib.h>
WORD AdlibPort = ADLIBPORT;
WORD AdlibChannels = ADLIBCHANNELS;
WORD AdlibStereo = 0;
/*
* Direct write to any Adlib/SB Pro II FM synthetiser register.
* reg - register number (range 0x001-0x0F5 and 0x101-0x1F5). When high byte
* of reg is zero, data go to port AdlibPort, otherwise to AdlibPort+2
* data - register value to be written
*/
BYTE WriteReg(WORD reg, BYTE data)
{
asm {
mov dx,AdlibPort
mov ax,reg
or ah,ah
jz out1
inc dx
inc dx
}
out1: asm {
out dx,al
mov cx,6
}
loop1:asm {
in al,dx
loop loop1
inc dx
mov al,data
out dx,al
dec dx
mov cx,36
}
loop2:asm {
in al,dx
loop loop2
}
return _AL;
}
/*
* Write to an operator pair. To be used for register bases of 0x20, 0x40,
* 0x60, 0x80 and 0xE0.
*/
void WriteChannel(BYTE regbase, BYTE channel, BYTE data1, BYTE data2)
{
static BYTE adlib_op[] = {0, 1, 2, 8, 9, 10, 16, 17, 18};
static BYTE sbpro_op[] = { 0, 1, 2, 6, 7, 8, 12, 13, 14,
18, 19, 20, 24, 25, 26, 30, 31, 32};
static WORD rg[] = {0x000,0x001,0x002,0x003,0x004,0x005,
0x008,0x009,0x00A,0x00B,0x00C,0x00D,
0x010,0x011,0x012,0x013,0x014,0x015,
0x100,0x101,0x102,0x103,0x104,0x105,
0x108,0x109,0x10A,0x10B,0x10C,0x10D,
0x110,0x111,0x112,0x113,0x114,0x115};
if (AdlibStereo)
{
register WORD reg = sbpro_op[channel];
WriteReg(rg[reg]+regbase, data1);
WriteReg(rg[reg+3]+regbase, data2);
} else {
register WORD reg = regbase+adlib_op[channel];
WriteReg(reg, data1);
WriteReg(reg+3, data2);
}
}
/*
* Write to channel a single value. To be used for register bases of
* 0xA0, 0xB0 and 0xC0.
*/
void WriteValue(BYTE regbase, BYTE channel, BYTE value)
{
static WORD ch[] = {0x000,0x001,0x002,0x003,0x004,0x005,0x006,0x007,0x008,
0x100,0x101,0x102,0x103,0x104,0x105,0x106,0x107,0x108};
register WORD chan;
if (AdlibStereo)
chan = ch[channel];
else
chan = channel;
WriteReg(regbase + chan, value);
}
/*
* Write frequency/octave/keyon data to a channel
*/
void WriteFreq(BYTE channel, WORD freq, BYTE octave, BYTE keyon)
{
WriteValue(0xA0, channel, (BYTE)freq);
WriteValue(0xB0, channel, *((BYTE *)&freq+1) | (octave << 2) | (keyon << 5));
}
/*
* Adjust volume value (register 0x40)
*/
BYTE convertVolume(BYTE data, WORD volume)
{
static BYTE volumetable[128] = {
0x00,0x01,0x03,0x05,0x06,0x08,0x0A,0x0B,
0x0D,0x0E,0x10,0x11,0x13,0x14,0x16,0x17,
0x19,0x1A,0x1B,0x1D,0x1E,0x20,0x21,0x22,
0x24,0x25,0x27,0x29,0x2B,0x2D,0x2F,0x31,
0x32,0x34,0x36,0x37,0x39,0x3B,0x3C,0x3D,
0x3F,0x40,0x42,0x43,0x44,0x45,0x47,0x48,
0x49,0x4A,0x4B,0x4C,0x4D,0x4F,0x50,0x51,
0x52,0x53,0x54,0x54,0x55,0x56,0x57,0x58,
0x59,0x5A,0x5B,0x5C,0x5C,0x5D,0x5E,0x5F,
0x60,0x60,0x61,0x62,0x63,0x63,0x64,0x65,
0x65,0x66,0x67,0x67,0x68,0x69,0x69,0x6A,
0x6B,0x6B,0x6C,0x6D,0x6D,0x6E,0x6E,0x6F,
0x70,0x70,0x71,0x71,0x72,0x72,0x73,0x73,
0x74,0x75,0x75,0x76,0x76,0x77,0x77,0x78,
0x78,0x79,0x79,0x7A,0x7A,0x7B,0x7B,0x7B,
0x7C,0x7C,0x7D,0x7D,0x7E,0x7E,0x7F,0x7F};
WORD n;
if (volume > 127)
volume = 127;
n = 63 - (data & 63);
n = (n*(int)volumetable[volume]) >> 7;
n = 63 - n;
return n | (data & 0xC0);
}
BYTE panVolume(WORD volume, int pan)
{
if (pan >= 0)
return volume;
else
return (volume * (pan+64)) / 64;
}
/*
* Write volume data to a channel
*/
void WriteVolume(BYTE channel, BYTE data[16], WORD volume)
{
WriteChannel(0x40, channel, ((data[6] & 1) ?
(convertVolume(data[5], volume) | data[4]) : (data[5] | data[4])),
convertVolume(data[12], volume) | data[11]);
}
/*
* Write pan (balance) data to a channel
*/
void WritePan(BYTE channel, BYTE data[16], int pan)
{
BYTE bits;
if (pan < -36) bits = 0x10; // left
else if (pan > 36) bits = 0x20; // right
else bits = 0x30; // both
WriteValue(0xC0, channel, data[6] | bits);
}
/*
* Write an instrument to a channel
*
* Instrument layout:
*
* Operator1 Operator2 Descr.
* data[0] data[7] reg. 0x20 - tremolo/vibrato/sustain/KSR/multi
* data[1] data[8] reg. 0x60 - attack rate/decay rate
* data[2] data[9] reg. 0x80 - sustain level/release rate
* data[3] data[10] reg. 0xE0 - waveform select
* data[4] data[11] reg. 0x40 - key scale level
* data[5] data[12] reg. 0x40 - output level
* data[6] reg. 0xC0 - feedback/AM-FM (both operators)
*/
void WriteInstrument(BYTE channel, BYTE data[16])
{
// WriteChannel(0x80, channel, 0x0F, 0x0F);
WriteChannel(0x40, channel, 0x3F, 0x3F); // no volume
WriteChannel(0x20, channel, data[0], data[7]);
WriteChannel(0x60, channel, data[1], data[8]);
WriteChannel(0x80, channel, data[2], data[9]);
WriteChannel(0xE0, channel, data[3], data[10]);
WriteValue (0xC0, channel, data[6] | 0x30);
}
/*
* Initialize hardware upon startup
*/
void InitAdlib(WORD port, WORD stereo)
{
WORD i;
AdlibPort = port;
if ( (AdlibStereo = stereo) != 0)
{
AdlibChannels = SBPROCHANNELS;
WriteReg(0x105, 0x01); // enable YMF262/OPL3 mode
WriteReg(0x104, 0x00); // disable 4-operator mode
WriteReg(0x01, 0x20); // enable Waveform Select
WriteReg(0x08, 0x40); // turn off CSW mode
WriteReg(0xBD, 0x00); // set vibrato/tremolo depth to low, set melodic mode
} else {
AdlibChannels = ADLIBCHANNELS;
WriteReg(0x01, 0x20); // enable Waveform Select
WriteReg(0x08, 0x40); // turn off CSW mode
WriteReg(0xBD, 0x00); // set vibrato/tremolo depth to low, set melodic mode
}
for(i = 0; i < AdlibChannels; i++)
{
WriteChannel(0x40, i, 0x3F, 0x3F); // turn off volume
WriteValue(0xB0, i, 0); // KEY-OFF
}
}
/*
* Deinitialize hardware before shutdown
*/
void DeinitAdlib(void)
{
WORD i;
for(i = 0; i < AdlibChannels; i++)
{
WriteChannel(0x40, i, 0x3F, 0x3F); // turn off volume
WriteValue(0xB0, i, 0); // KEY-OFF
}
if (AdlibStereo)
{
WriteReg(0x105, 0x00); // disable YMF262/OPL3 mode
WriteReg(0x104, 0x00); // disable 4-operator mode
WriteReg(0x01, 0x20); // enable Waveform Select
WriteReg(0x08, 0x00); // turn off CSW mode
WriteReg(0xBD, 0x00); // set vibrato/tremolo depth to low, set melodic mode
} else {
WriteReg(0x01, 0x20); // enable Waveform Select
WriteReg(0x08, 0x00); // turn off CSW mode
WriteReg(0xBD, 0x00); // set vibrato/tremolo depth to low, set melodic mode
}
}
/*
* Write to a mixer register -- SP Pro only
*/
void SetMixer(BYTE index, BYTE data)
{
if (AdlibStereo)
asm {
mov dx,AdlibPort
add dx,4 // port 224h - Mixer register index
mov al,index
out dx,al
inc dx // port 225h - Mixer data
mov al,data
out dx,al
}
}
/*
* Read from a mixer register -- SP Pro only
*/
int GetMixer(BYTE index)
{
if (AdlibStereo)
asm {
mov dx,AdlibPort
add dx,4 // port 224h - Mixer register index
mov al,index
out dx,al
inc dx // port 225h - Mixer data
in al,dx
xor ah,ah
} else
_AX = -1;
return _AX;
}
/*
* Detect SB Pro mixer
*/
int DetectMixer(WORD port)
{
WORD origPort = AdlibPort;
WORD origStereo = AdlibStereo;
WORD origvolume, volume1, volume2;
AdlibPort = port;
AdlibStereo = 1;
origvolume = GetMixer(0x26); // FM Volume
SetMixer(0x26, origvolume ^ 0xAA);
volume1 = GetMixer(0x26) & 0xEE;
SetMixer(0x26, origvolume ^ 0x44);
volume2 = GetMixer(0x26) & 0xEE;
SetMixer(0x26, origvolume);
AdlibPort = origPort;
AdlibStereo = origStereo;
return ((volume1 ^ 0xAA) == (volume2 ^ 0x44));
}
/*
* Detect Adlib card
*/
int DetectAdlib(WORD port)
{
WORD origPort = AdlibPort;
WORD stat1, stat2, i;
AdlibPort = port;
WriteReg(0x04, 0x60);
WriteReg(0x04, 0x80);
stat1 = inportb(port) & 0xE0;
WriteReg(0x02, 0xFF);
WriteReg(0x04, 0x21);
for (i = 512; --i; )
inportb(port);
stat2 = inportb(port) & 0xE0;
WriteReg(0x04, 0x60);
WriteReg(0x04, 0x80);
AdlibPort = origPort;
return (stat1 == 0 && stat2 == 0xC0);
}
/*
* Detect Sound Blaster Pro II (with OPL3)
*/
int DetectSBProII(WORD port)
{
if (!DetectAdlib(port))
return 0;
if (!DetectMixer(port))
return 0;
if (DetectAdlib(port+2))
return 0;
return 1;
}
/*
* Parse BLASTER environment variable
*/
int DetectBlaster(WORD *port, BYTE *irq, BYTE *dma, BYTE *type)
{
const char *blaster = getenv("BLASTER");
if (!blaster) return 0;
while (*blaster)
{
char *endpos = NULL;
long value;
switch (*blaster++) {
case 'A': // base I/O address
value = strtol(blaster, &endpos, 16);
if (port) *port = value;
break;
case 'I': // IRQ number
value = strtol(blaster, &endpos, 0);
if (irq) *irq = value;
break;
case 'D': // DMA number
value = strtol(blaster, &endpos, 0);
if (dma) *dma = value;
break;
case 'T': // card type
value = strtol(blaster, &endpos, 0);
if (type) *type = value;
break;
// ignore anything else (spaces, ...)
}
if (endpos)
blaster = endpos;
}
return 1;
}